// Have include guard.
#ifndef FPK_OPERATION_HPP
#define FPK_OPERATION_HPP

// Include files.
#include <boost/smart_ptr/shared_ptr.hpp>
#include "Subscriber.hpp"

struct Unspecified
{
};

template<typename Result>
class Operation;

/**
 *  The Operation<Unspecified> class is the base class for all Operations.
 *  
 *  It is unspecified what is the type of the returned result. 
 */
template<>
class Operation<Unspecified> :
	public Subscriber
{
	friend class Subscriber;
	
	template<typename Result>
	friend class Operation;
	
	private:
		bool m_isFinished;
		boost::shared_ptr<Subscriber > m_pointerSelfHack;
		mutable ReentrantSemaphore m_semaphore;
		mutable std::stack<int> m_tasksWaiting;
	public:
		/**
		 *  Constructor for Operation<Unspecified>.
		 */
		Operation() :
			m_isFinished(false)
		{
			SetIsSubscribed(true);
		}
		
		/**
		 *  Get whether the Operation has finished.
		 *  
		 *  @returns True if the Operation is finsihed or false if otherwise.
		 */
		bool GetIsFinished() const
		{
			bool isFinished;
			
			m_semaphore.take();
			
			isFinished = m_isFinished;
			
			m_semaphore.give();
			
			return isFinished;
		}
		
		/**
		 *  Wait for the Operation to finish.
		 *  
		 *  Warning: Calling this in the periodic loop will stall the program.
		 *  Consider using GetIsFinished instead.
		 */
		void Wait() const
		{
			if (GetIsFinished())
				return;
			
			int taskId = taskIdSelf();
			
			m_semaphore.take();
			m_tasksWaiting.push(taskId);
			m_semaphore.give();
			
			/*
			 * Hmm, on the one hand, this has to be outside the semaphore lock
			 * to avoid a deadlock.  However, after pushing the task to the stack,
			 * but before suspending, the operation could finish.  Which would cause
			 * this task to get suspended forever with nothing to resume it.
			 */
			taskSuspend(taskId);
		}
		
		/**
		 *  Cancel the Operation and finish it.
		 */
		void Cancel()
		{
			CancelInternal();
		}
	protected:
		/**
		 *  Override this function to handle the Operation's launching.
		 */
		virtual void HandleLaunch()
		{
		}
	private:
		/**
		 *  Run internal code to handle finish.
		 */
		void FinishInternal()
		{
			m_semaphore.take();
			
			m_isFinished = true;
			
			while (m_tasksWaiting.size() > 0)
			{
				taskResume(m_tasksWaiting.top());
				m_tasksWaiting.pop();
			}
			
			m_parent->RemoveOperation(this);
			
			m_semaphore.give();
		}
		
		/**
		 *  Run internal code to handle cancel.
		 */
		virtual void CancelInternal()
		{
		}
};

/**
 *  An Operation is a RobotSubscriber that is launched by another RobotSubscriber and returns a result when the operation is finished.
 *  
 *  This class is untested.
 *  
 *  @tparam Result The type of the returned result.
 */
template<typename Result = void>
class Operation :
	public Operation<Unspecified>
{
	private:
		Result m_result;
	public:
		/**
		 *  Constructor for Operation.
		 */
		Operation(Result cancelResult = Result()) :
			m_result(cancelResult)
		{
		}
		
		/**
		 *  Wait for the Operation to finish.
		 *  
		 *  Warning: Calling this in the periodic loop will stall the program.
		 *  Consider using GetIsFinished instead.
		 *  
		 *  @return The result.
		 */
		Result Wait() const
		{
			Operation<Unspecified>::Wait();
			
			return m_result;
		}
	protected:
		/**
		 *  Finish the Operation.
		 *  
		 *  @param result The result.
		 */
		void Finish(Result result)
		{
			m_result = result;
			FinishInternal();
		}
		
		/**
		 *  Override this function to handle the Operation getting canceled.
		 *  
		 *  @return The result.
		 */
		virtual Result HandleCancel()
		{
			return Result();
		}
	private:
		/**
		 *  Run internal code to handle cancel.
		 */
		virtual void CancelInternal()
		{
			Finish(HandleCancel());
		}
};

/**
 *  An Operation<void> is an Operation that returns void (doesn't return anything).
 *  
 *  This class is untested.
 */
template<>
class Operation<void> :
	public Operation<Unspecified>
{
	protected:
		/**
		 *  Finish the Operation.
		 */
		void Finish()
		{
			FinishInternal();
		}
		
		/**
		 *  Override this function to handle the Operation getting canceled.
		 */
		virtual void HandleCancel()
		{
		}
	private:
		/**
		 *  Run internal code to handle cancel.
		 */
		virtual void CancelInternal()
		{
			HandleCancel();
			Finish();
		}
};

#endif // #ifndef FPK_OPERATION_HPP
